package org.wheres2.location;

import java.util.Date;

import org.wheres2.eventmanager.PositionManager;

import android.app.Service;
import android.content.Context;
import android.content.Intent;
import android.location.Location;
import android.location.LocationListener;
import android.location.LocationManager;
import android.os.Binder;
import android.os.Bundle;
import android.os.IBinder;
/**
 * La classe impelmenta l'Android Service che si occupa della gestione del sensore GPS
 * 
 * @author 
 *
 */
public class ServiceLocation extends Service implements CustomLocationManager {

	private static final int THREE_MINUTES = 1000 * 60 * 3;

	private final IBinder ServiceLocationBinderIstance = new ServiceLocationBinder();
	
	private LocationManager locManager;
	private Location currentLocation = null;
	private Location oldLocation = null;
	private PositionManager positionListener = null;
	private boolean isLocationStarted = false;
	
	public class ServiceLocationBinder extends Binder{
		
		public ServiceLocation getService(){
			return ServiceLocation.this;
		}
		
	}
	
	LocationListener netLocationListener = new LocationListener(){

		@Override
		public void onLocationChanged(Location location) {
			if(positionListener != null){
				currentLocation = compareLocations(oldLocation, location);
				oldLocation = currentLocation;
				positionListener.updateLocation(currentLocation);
			}
			
		}

		@Override
		public void onProviderDisabled(String provider) {
			
		}

		@Override
		public void onProviderEnabled(String provider) {
			
		}

		@Override
		public void onStatusChanged(String provider, int status, Bundle extras) {
			
		}
		
	};
	
	LocationListener gpsLocationListener = new LocationListener(){

		@Override
		public void onLocationChanged(Location location) {
			if(positionListener != null){
				currentLocation = compareLocations(oldLocation, location);
				oldLocation = currentLocation;
				positionListener.updateLocation(currentLocation);
			}
		}

		@Override
		public void onProviderDisabled(String provider) {
			
		}

		@Override
		public void onProviderEnabled(String provider) {
			
		}

		@Override
		public void onStatusChanged(String provider, int status, Bundle extras) {
			
		}
		
	};

	@Override
	public int onStartCommand(Intent intent, int flags, int startId) {
		locManager = (LocationManager) this.getSystemService(Context.LOCATION_SERVICE);
		return START_STICKY;
	}
	
	
	
	@Override
	public boolean onUnbind(Intent intent) {
		locManager.removeUpdates(gpsLocationListener);
		locManager.removeUpdates(netLocationListener);
		this.positionListener = null;
		return super.onUnbind(intent);
	}
	
	
	@Override
	public IBinder onBind(Intent intent) {
		return ServiceLocationBinderIstance;
	}
	
	
	/**
	 * 
	 * @see org.wheres2.location.CustomLocationManager#startLocation()
	 * 
	 */
	@Override
	public void startLocation(){
		
		Location lastNetLocation = null;
		Location lastGPSLocation = null;
		if(locManager.isProviderEnabled(LocationManager.NETWORK_PROVIDER)){
			locManager.requestLocationUpdates(LocationManager.NETWORK_PROVIDER, 3000, 0, netLocationListener);
			lastNetLocation = locManager.getLastKnownLocation(LocationManager.NETWORK_PROVIDER);
		}
		
		locManager.requestLocationUpdates(LocationManager.GPS_PROVIDER, 3000, 0, gpsLocationListener);
		lastGPSLocation = locManager.getLastKnownLocation(LocationManager.GPS_PROVIDER);
		this.isLocationStarted = true;
		oldLocation = this.compareLocations(lastGPSLocation, lastNetLocation);
	}
	
	/**
	 * 
	 * @see org.wheres2.location.CustomLocationManager#stopLocation()
	 * 
	 */
	@Override
	public void stopLocation(){
		locManager.removeUpdates(gpsLocationListener);
		locManager.removeUpdates(netLocationListener);
		this.isLocationStarted = false;
	}
	
	/**
	 * 
	 * @see org.wheres2.location.CustomLocationManager#isLocationStarted()
	 * 
	 */
	@Override
	public boolean isLocationStarted() {
		return this.isLocationStarted;
	}
	
	/**
	 *
	 * @see org.wheres2.location.CustomLocationManager#getLocation()
	 * 
	 */
	@Override
	public Location getLocation(){
		Location returnLocation = this.compareLocations(oldLocation, currentLocation);
		if (returnLocation != null){
			Date t = new Date();
			long time = t.getTime() - returnLocation.getTime();
			if(time > THREE_MINUTES){
				return null;
			}
		}
		return returnLocation;
	}
	
	/**
	 * 
	 * @see org.wheres2.location.CustomLocationManager#isGPSProviderEnabled()
	 * 
	 */
	@Override
	public boolean isGPSProviderEnabled(){
		return this.locManager.isProviderEnabled(LocationManager.GPS_PROVIDER);
	}
	
	/**
	 * 
	 * @see org.wheres2.location.CustomLocationManager#isNetProviderEnabled()
	 * 
	 */
	@Override
	public boolean isNetProviderEnabled(){
		return this.locManager.isProviderEnabled(LocationManager.NETWORK_PROVIDER);
	}
	
	/**
	 * 
	 * Confronta due diverse locazioni e determina la migliore
	 * 
	 * @param firstLocation la prima posizione 
	 * @param secondLocation la seconda posizione
	 * 
	 * @return Location la posizone migliore
	 * 
	 */
	private Location compareLocations(Location firstLocation, Location secondLocation){
		if(firstLocation == null && secondLocation == null)
			return null;
		else if(firstLocation == null){
			return secondLocation;
		}else if(secondLocation == null){
			return firstLocation;
		}
	    long timeDelta = - firstLocation.getTime() + secondLocation.getTime();
	    boolean isSignificantlyNewer = timeDelta > THREE_MINUTES;
	    boolean isSignificantlyOlder = timeDelta < -THREE_MINUTES;
	    boolean isNewer = timeDelta > 0;
	    
	    int accuracyDelta = (int) (firstLocation.getAccuracy() - secondLocation.getAccuracy());
	    boolean secondMoreAccurate = accuracyDelta > 0;
	    boolean firstMoreAccurate = accuracyDelta < 0;
	    boolean secondSignificantlyMoreAccurate = accuracyDelta > 200;
	    
	 // Check if the old and new location are from the same provider
	    boolean isFromSameProvider = isSameProvider(firstLocation.getProvider(),
	    		secondLocation.getProvider());
	    if(isSignificantlyNewer){
	    	return secondLocation;
	    }else if(isSignificantlyOlder){
	    	return firstLocation;
	    }
	    
	    if(secondMoreAccurate){
	    	return secondLocation;
	    }else if(isNewer && !firstMoreAccurate){
	    	return secondLocation;
	    }else if(isNewer && !secondSignificantlyMoreAccurate && isFromSameProvider){
	    	return secondLocation;
	    }	
		return firstLocation;
	}
	
	/**
	 * 
	 * Determina se i due provider in ingresso coincidono 
	 * 
	 * @param provider1 il primo provider
	 * @param provider2 il secondo provider
	 * 
	 * @return true se i due provider coincidono
	 * 
	 */
	private boolean isSameProvider(String provider1, String provider2) {
	    if (provider1 == null) {
	      return provider2 == null;
	    }
	    return provider1.equals(provider2);
	}
	
	/**
	 * aggiunge un position Listener
	 * 
	 * @param listener un oggetto di tipo PositionManager
	 * 
	 */
	public void addPositionListener(PositionManager listener){
		this.positionListener = listener;
	}
	

}
